package com.love.rongcommon.util;

import cn.hutool.core.net.NetUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 1bit: 不用,因为二进制中最高位是符号位,1表示负数,0表示正数,生成的id一般都是用整数,所以最高位固定为0.
 * 41bit-时间戳: 用来记录时间戳,毫秒级.41位可以表示2(41)-1个数字;如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0至2(41)-1,减1是因为可表示的数值范围是从0开始算的,而不是1'也就是说41位可以表示2(41)-1个毫秒的值,转化成单位年则是2(41)-1/(1000606024365)=69年(够用到2039-09-07)
 * 10bit-工作机器id: 用来记录工作机器的id.可以部署在2(10)=1024个节点,包括5位datacenterId和5位workerId;5位(bit)可以表示的最大正整数是2(5)-1=31,即可以用0,1,2,3,...31这32个数字,来表示不同的datacenterId和workerId
 * 12bit-序列号: 用来记录同一毫秒内产生的不同id.12位(bit)可以表示最大正整数是2^{12}-1=4095,即可以用0,1,2,3...4094这4095个数字,来表示同一时间戳内产生的4095个ID序号
 */
@Slf4j
public class SnowFlake {
    private static SnowFlake instance = new SnowFlake();

    public static SnowFlake getInstance() {
        return instance;
    }

    /**
     * 起始的时间戳
     */
    private final long START_TIMESTAMP = 1577808000000L;

    /**
     * 序列号占用位数
     */
    private final int SEQUENCE_BIT = 12;
    /**
     * 机器标识占用位数
     */
    private final int MACHINE_BIT = 10;
    /**
     * 时间戳位移位数
     */
    private final int TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    /**
     * 最大序列号  （4095）
     */
    private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
    /**
     * 最大机器编号 （1023）
     */
    private final long MAX_MACHINE_ID = ~(-1L << MACHINE_BIT);
    /**
     * 生成id机器标识部分
     */
    private long machineIdPart;
    /**
     * 序列号
     */
    private long sequence = 0L;
    /**
     * 上一次时间戳
     */
    private long lastStamp = -1L;

    /**
     * 构造函数初始化机器编码
     */
    private SnowFlake() {
        //localIp & MAX_MACHINE_ID最大不会超过1023,在左位移12位
        long machineId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr());
        machineIdPart = (machineId & MAX_MACHINE_ID) << SEQUENCE_BIT;
    }

    /**
     * 获取雪花ID
     */
    public synchronized long nextId() {
        long currentStamp = timeGen();
        //避免机器时钟回拨
        while (currentStamp < lastStamp) {
            // //服务器时钟被调整了,ID生成器停止服务.
            throw new RuntimeException(String.format("时钟已经回拨.  Refusing to generate id for %d milliseconds", lastStamp - currentStamp));
        }
        if (currentStamp == lastStamp) {
            // 每次+1
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 毫秒内序列溢出
            if (sequence == 0) {
                // 阻塞到下一个毫秒,获得新的时间戳
                currentStamp = getNextMill();
            }
        } else {
            //不同毫秒内，序列号置0
            sequence = 0L;
        }
        lastStamp = currentStamp;
        //时间戳部分+机器标识部分+序列号部分
        return (currentStamp - START_TIMESTAMP) << TIMESTAMP_LEFT | machineIdPart | sequence;
    }
    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     */
    private long getNextMill() {
        long mill = timeGen();
        while (mill <= lastStamp) {
            mill = timeGen();
        }
        return mill;
    }
    /**
     * 返回以毫秒为单位的当前时间
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 从雪花id中计算出规定起始时间戳的时间戳
     * 起始时间戳见EPOCH字段
     *
     * @param snowId 雪花算法生成的id
     * @return
     */
    public long timestamp(long snowId) {
        String bid = Long.toBinaryString(snowId);
        int timestamp = bid.length() - (MACHINE_BIT + SEQUENCE_BIT);
        String substring = bid.substring(0, timestamp);
        return datetimeByTimestamp(Long.parseUnsignedLong(substring, 2));
    }

    /**
     * 根据雪花id的时间戳，计算真实时间戳，从1970.。。开始
     *
     * @param timestamp 雪花算法的时间戳
     * @return
     */
    private long datetimeByTimestamp(long timestamp) {
        return timestamp + START_TIMESTAMP;
    }

    /**
     * 测试
     */
    public static void main(String[] args) throws InterruptedException {
//        //计时开始时间
//        long start = System.currentTimeMillis();
//        //让100个线程同时进行
//        final CountDownLatch latch = new CountDownLatch(100);
//        //判断生成的10万条记录是否有重复记录
//        final Map<Long, Integer> map = new ConcurrentHashMap();
//        for (int i = 0; i < 100; i++) {
//            //创建100个线程
//            new Thread(() -> {
//                for (int s = 0; s < 2000; s++) {
//                    long snowID = SnowFlake.getInstance().nextId();
//                    System.err.println(snowID);
//                    Integer put = map.put(snowID, 1);
//                    if (put != null) {
//                        throw new RuntimeException("主键重复");
//                    }
//                }
//                latch.countDown();
//            }).start();
//        }
//        //让上面100个线程执行结束后，在走下面输出信息
//        latch.await();
//        log.info("生成20万条雪花ID总用时={}", System.currentTimeMillis() - start);

        System.err.println(NetUtil.getLocalhostStr());
        System.err.println(NetUtil.ipv4ToLong(NetUtil.getLocalhostStr()));

//        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
//        SnowFlake instance = SnowFlake.getInstance();
//        long id = instance.nextId();
//        LocalDateTime dateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(instance.timestamp(id)), ZoneId.systemDefault());
//        System.out.println(id);
//        System.out.println(dateTime.format(formatter));
    }
}
